package logic

import (
	"context"
	"database/sql"
	"errors"
	"fmt"
	"github.com/zeromicro/go-zero/core/stores/sqlx"
	"short/model"
	"short/pkg/base62"
	"short/pkg/connect"
	"short/pkg/md5"
	"short/pkg/urltool"

	"short/internal/svc"
	"short/internal/types"

	"github.com/zeromicro/go-zero/core/logx"
)

type ConvertHandleLogic struct {
	logx.Logger
	ctx    context.Context
	svcCtx *svc.ServiceContext
}

func NewConvertHandleLogic(ctx context.Context, svcCtx *svc.ServiceContext) *ConvertHandleLogic {
	return &ConvertHandleLogic{
		Logger: logx.WithContext(ctx),
		ctx:    ctx,
		svcCtx: svcCtx,
	}
}

func (l *ConvertHandleLogic) ConvertHandle(req *types.ConvertReq) (resp *types.ConvertResp, err error) {
	// todo: add your logic here and delete this line
	//参数校验放在handle里，使用validator
	//校验url是否可用

	if ok := connect.Get(req.LongUrl); !ok {
		return nil, errors.New("无效的链接")
	}
	//判断之前这个连接是否转链接过（数据库中是否有该连接）
	mlong := md5.NewSum([]byte(req.LongUrl))
	_, err = l.svcCtx.ShortUrl.FindOneByMd5(l.ctx, sql.NullString{
		String: mlong,
		Valid:  true,
	})
	if err != sqlx.ErrNotFound {
		if err == nil {
			return nil, errors.New("该链接已经是短链了")
		}
		fmt.Println(err)
		logx.Errorw("shortUrl.FindOneByMd5 failed", logx.LogField{
			Key:   "err",
			Value: err.Error(),
		})
		return nil, err
	}

	//判断是否为短链，防止重复转链
	basePath, err := urltool.GetBasePath(req.LongUrl)
	if err != nil {
		return nil, err
	}
	_, err = l.svcCtx.ShortUrl.FindOneBySurl(l.ctx, sql.NullString{String: basePath, Valid: true})
	if err != sqlx.ErrNotFound {
		if err == nil {
			return nil, fmt.Errorf("该链接已经是短链了")
		}
		logx.Errorw("hortUrl.FindOneBySurl failed", logx.LogField{
			Key:   "err",
			Value: err.Error(),
		})
		return nil, err
	}
	//进行转链接
	var short string
	for {
		//取号器,基于Mysql实现
		seq, err := l.svcCtx.Sequence.Next()
		if err != nil {
			logx.Errorw("Sequence.Next failed", logx.LogField{Key: "err", Value: err.Error()})
			return nil, err
		}

		//号码转换为短链
		short = base62.Transport62(seq)
		//为了安全性，设置了一个黑名单
		//不在黑名单，退出循环
		if _, ok := l.svcCtx.ShortBlackmap[short]; !ok {
			break
		}
	}
	//长短链接存入数据库
	if _, err = l.svcCtx.ShortUrl.Insert(l.ctx, &model.ShortUrlMap{
		Lurl: sql.NullString{String: req.LongUrl, Valid: true},
		Md5:  sql.NullString{String: mlong, Valid: true},
		Surl: sql.NullString{String: short, Valid: true},
	}); err != nil {
		logx.Errorw("ShortUrl.Insert failed", logx.LogField{Key: "err", Value: err.Error()})
		return nil, err
	}
	//将短链接加入布隆过滤器
	err = l.svcCtx.Filter.Add([]byte(short))
	if err != nil {
		logx.Errorw("Filter.Add failed", logx.LogField{Key: "err", Value: err.Error()})
	}
	//返回响应为短域名+短链接
	shortURL := l.svcCtx.Config.ShortDoamin + short
	return &types.ConvertResp{ShortUrl: shortURL}, nil
}
